// 引入帮我们实现 单页面应用(SPA)  的第三方包
import SEMRouter from 'sme-router'
// 引入模板
import index from "@v"
import login from "@v/login"
import adminList from "@v/adminList"
import advList from "@v/advList"
// 引入发送请求的函数
import { GetAdmins, AddAdmin, DelOneAdmin , updataAdmin } from '@f/adminApi'
import { GetAdv, AddAdv, DelAdv, GetOneAdv, UpdataAdv } from '@f/advApi'
import { reqlogin, reqlogout, reqverifyToken } from '@f/loginApi'
import { AdvTimeFormat, AdminTimeFormat } from '../utils/timeformat'
// 创建router对象
const router = new SEMRouter('root', 'html5')
// 把router对象挂在全局windows对象上，解决模块化 局部变量 不能在其他模块应用的问题
window.router = router
// 声明一个当前作用域的变量，用来存储商品的ID
// 声明一个当前作用域的变量，用来存储管理员的ID
let updataAdvId = ''
let updataAdminId = ''
// ✨ 前端路由(登陆页面/一级路由)
router.route('/login', function (req, res) {
    // 渲染login页面
    res.render(login())
    // 注意：要先渲染login页面再获取dom节点，顺序不能乱

    // 去login.ejs中找到登录按钮节点并添加一个点击事件
    // 😡 当我们点击登录按钮的时候，向服务器发送请求，先考虑是不是请求没发送成功，但这样的考虑被打破了，因为toastr弹出来了
    // 😡 说明在此处请求发送成功了，因此去请求成功的代码找问题
    $('#login_btn').on('click', async (e) => {
        // 阻止默认行为
        e.preventDefault()
        // 1.接收输入框传输过来的账号和密码
        // trim() 是用来去除空格的
        const username = $('#username').val().trim()
        const password = $('#password').val().trim()
        // 2.封装发送请求的函数并且调用(该函数实则为axios创建的实例，并且将axios封装的返回结果处理为我们需要的数据)
        const result = await reqlogin(username, password)
        // console.log(result)
        // 如果状态码为1，即为登陆成功，首先要做的是跳转
        if (result.code) {

            // 😡 这里这里！我们发现，请求成功后返回的result的code为1，然后按照执行顺序，首先跳转，然后弹框，再存储
            // 😡 显然，先跳转会执行前端路由的index代码，而login代码并没有执行到存储用户信息的逻辑，所以，又会执行61行的逻辑
            // 😡 跳回到登录页，所以，这里只用作简单的调整，先存储token和用户信息，在跳转，index就可以拿到信息了
            // 😜 完美解决！

            // 将token存储到localStorage中
            localStorage.setItem("token", result.token)
            // 将用户信息存储到localStorage中
            localStorage.setItem("user", JSON.stringify(result.user))
            // 跳转到首页页面 , 第二个参数(并将数据传输到index页面)传输位置为另一个页面的请求报文中
            router.go('/index', { username: result.user.username })
            // 提醒用户登陆成功
            // 此处应用了一个toastr工具帮助我们生成好看的弹框
            toastr.success(result.msg)
        } else { // 状态码为0
            // 提醒用户登陆失败
            toastr.error(result.msg)
        }
    })
})
// ✨ 前端路由(首页页面/一级路由)
router.route('/index', async function (req, res, next) {

    // 😡 debugger
    // 😡 当有用户直接在浏览器地址栏访问index页面时(当localStorage为空时，模拟第一次登陆)，则此处的id读不到值，id前的内容为null
    // 😡 所以先进行判断，如果为null,则直接跳转到login页面
    if (!JSON.parse(localStorage.getItem('user'))) return router.go('/login')
    // 😡 但是当解决了这一问题，又出现了新的问题，用户第一次登陆时登陆不上，所以我们跳到前端路由规则的登录接口查找问题

    // 验证token是否存在，是否正确，是否过期，如果其一不符合，则跳转到login页面
    let tokresult = await reqverifyToken(JSON.parse(localStorage.getItem('user')).id)
    if (!tokresult.code) {
        toastr.error(tokresult.msg)
        router.go('/login')
        return
    }
    // console.log(res.subRoute())
    // console.log(location,req)
    // location {...} location.pathname
    // req {...} req.url
    // console.log(res)
    // console.log(req)
    next(
        index({
            // 将当前的浏览器地址栏的url传入到index模板中
            // 也可以使用location.pathname来处理
            url: req.url,
            subRoute: res.subRoute(), // res.subRoute => <div id="__sub-route-view"></div>
            username: req.body.username //将用户名的数据传输到index.ejs的模板中
        })
    )
    // 退出登录逻辑
    // 1.点击退出登录按钮
    $('#logoutBtn').on('click', async () => {
        // 2.删除localStorage中的token
        localStorage.setItem('token', '')
        // 3.删除数据库中的token 调用我们自己封装的axios函数，将参数ID传输进去，并且等待axios异步请求成功的结果
        await reqlogout(JSON.parse(localStorage.getItem('user')).id)
        // 4.将退出信息提示给用户
        toastr.success('退出登录')
        // 5.跳转到登陆页面
        router.go('/login')
    })
})
// ✨ 配置嵌套的前端路由规则(二级路由)
router.route('/index/adminList', async (req, res) => {
    // 验证token是否存在，是否正确，是否过期，如果其一不符合，则跳转到login页面
    let tokresult = await reqverifyToken(JSON.parse(localStorage.getItem('user')).id)
    if (!tokresult.code) {
        toastr.error(tokresult.msg)
        router.go('/login')
        return
    }
    // 🐷此处的result的值为 我们创建的axios实例并且结果是经过处理的axios返回值
    let result = await GetAdmins()
    // console.log(result)
    // 修改时间类型，并使用dayjs或者moment第三方模块来转换时间格式为预期的时间格式
    AdminTimeFormat(result.data)
    // 此处result.data的值会渲染到管理员列表页面，并且result.data传到adminlist.ejs中，和data等同
    res.render(adminList(result.data))

    // 渲染完成之后，再对管理员进行增、删、改工作
    // 🐷添加管理员
    // 🚩这里，由于修改和添加为同一个按钮，因此写在一起
    $('.proxy-parent').on('click', '#saveAdmin', async (e) => {
        let result
        // 创建formdata实例
        const formdata = new FormData($('#addForm')[0])
        if (e.target.innerText === '保存') {
            // 验证表单
            if (
                !formdata.get('username') ||
                !formdata.get('password') ||
                !formdata.get('confirm-pw')
            ) return toastr.error('请正确填写表单')
            if (
                formdata.get('password') !== formdata.get('confirm-pw')
            ) return toastr.warning('两次输入密码不一致，请重新输入密码')
            // 将用户id添加到formdata中
            formdata.append('id', JSON.parse(localStorage.getItem('user')).id)
            // 发送请求,获取请求的结果
            result = await AddAdmin(formdata)
        }else{
            // 验证表单
            if (
                !formdata.get('password') ||
                !formdata.get('confirm-pw')
            ) return toastr.error('请正确填写表单')
            if (
                formdata.get('password') !== formdata.get('confirm-pw')
            ) return toastr.warning('两次输入密码不一致，请重新输入密码')
            // 将当前管理员的ID和要修改密码的管理员ID加入到formdata对象中
            formdata.append('id', JSON.parse(localStorage.getItem('user')).id)
            formdata.append('adminId',updataAdminId)
            // 发送请求
            result = await updataAdmin(formdata)
        }
        // 根据状态码判断是否请求成功
        if (result.code) {
            //1.提示用户
            toastr.success(result.msg)
            //2.修改时间格式
            AdminTimeFormat(result.admins)
            //3.清空(模态框)表单里的内容
            $('#addForm')[0].reset()
            //4.重新渲染
            res.render(adminList(result.admins))
        } else {
            toastr.error(result.msg)
            router.go('/index')
        }
    })

    // 🐷删除管理员
    $('.proxy-parent').on('click', '.delAdmin', async (e) => {
        // 发送删除的请求，1.删除哪一条数据？在渲染二级路由的时候，
        // 把每个管理员的id添加到对应的那一行的删除按钮的id属性上
        // 获取管理员id = e.target.id
        const adminId = e.target.id
        const result = await DelOneAdmin(adminId)
        // 1.提示用户
        toastr.error(result.msg)
        // 2.重新渲染页面
        res.render(adminList(result.admins))
    })

    // 🐷修改管理员密码
    $('.proxy-parent').on('click', '#modifyPw', (e) => {
        // 1.修改模态框的标题和按钮上边的文字
        $('#addOrModify').text('修改管理员密码')
        $('#saveAdmin').text('修改')
        // 2.获取修改按钮上的数据
        const adminId = e.target.dataset.id
        const username = e.target.dataset.username
        // 3.将username渲染到模态框上
        $('input[name=username]').val(username)
        // 4.设置为只读
        $('input[name=username]').attr('readonly', 'readonly')
        // 🚩5.发送请求 在124行
        updataAdminId = adminId
    })

    $('.proxy-parent').on('click', '#ADD', () => {
        // 1.修改模态框的标题和按钮上边的文字
        $('#addOrModify').text('添加管理员')
        $('#saveAdmin').text('保存')
        // 2.取消只读属性
        $('input[name=username]').removeAttr('readonly')
        //3.清空(模态框)表单里的内容
        $('#addForm')[0].reset()

    })

})
// ✨ 配置嵌套的前端路由规则(二级路由)
router.route('/index/advList', async (req, res) => {
    // 🐷 验证token是否存在，是否正确，是否过期，如果其一不符合，则跳转到login页面
    let tokresult = await reqverifyToken(JSON.parse(localStorage.getItem('user')).id)
    if (!tokresult.code) {
        toastr.error(tokresult.msg)
        router.go('/login')
        return
    }
    // 🐷 渲染页面
    // 这里的result就是我们前端经过axios请求，发送到后端指定的接口，后端res.send()中的对象
    let result = await GetAdv()
    // console.log(result)
    // 此处修改时间类型，修改为我们预期的时间类型，渲染到advlist.ejs模板中(使用dayjs插件)
    // console.log(result.data)
    AdvTimeFormat(result.data)
    // 此处的result.data的值会渲染到广告列表界面，并且result.data传到advlist.ejs中，和data等同
    res.render(advList(result.data))

    // 🐷 添加广告
    // 😝 还有这里也需要委托
    // 🚩 由于修改和保存是同一个模态框的按钮，因此首先我们要先判断一下到底是修改还是保存
    $('.proxy-parent').on('click', '#addOrUpdateAdv', async function (e) {
        // ① 先获取表单数据
        // 由于读不到块级作用域的result，所以在外边声明result
        let result
        const formdata = new FormData($('#form')[0])
        // ②
        if (e.target.innerText === '添加') {
            // 1.验证表单(是否每个输入框都有值)x
            if (
                !formdata.get('advTitle') ||
                !formdata.get('advCate') ||
                !formdata.get('advLink') ||
                !formdata.get('files').size
            )
                return toastr.error('请正确填写表单')
            // 2.发送formdata请求
            // 为了数据安全起见，在对广告的每次增删改查之前校验一下token，所以在服务端的advRouter.js中的增删改查
            // 的接口之前增加了一个中间件，来校验token的有效性，而校验token不仅需要token本身，还需要用户id
            // 所以此处把用户的id添加到formdata对象中
            formdata.append('id', JSON.parse(localStorage.getItem('user')).id)
            result = await AddAdv(formdata)
        } else {
            // 1.验证表单(是否每个输入框都有值)
            if (
                !formdata.get('advTitle') ||
                !formdata.get('advCate') ||
                !formdata.get('advLink')
            ) return toastr.error('请正确填写表单')
            // 2.发送formdata请求
            // 为了数据安全起见，在对广告的每次增删改查之前校验一下token，所以在服务端的advRouter.js中的增删改查
            // 的接口之前增加了一个中间件，来校验token的有效性，而校验token不仅需要token本身，还需要用户id
            // 所以此处把用户的id添加到formdata对象中
            formdata.append('advId', updataAdvId)
            formdata.append('id', JSON.parse(localStorage.getItem('user')).id)
            // 3.定义发送修改广告的请求和后端路由接口
            result = await UpdataAdv(formdata)
        }
        // ③
        if (result.code) {
            // 当请求成功之后，要把form表单中的数据清空
            $('#form')[0].reset()
            // 并把预览图也清空
            $('#showImg')[0].src = ''
            // 4.将请求的结果反馈给用户
            toastr.success(result.msg)
            // 5.根据返回的最新数据，重新渲染到页面上
            // 此处修改时间类型，修改为我们预期的时间类型，渲染到advlist.ejs模板中(使用dayjs插件)
            AdvTimeFormat(result.advs)
            res.render(advList(result.advs))
        } else {
            toastr.error(result.msg)
            router.go('/login')
        }
    })

    // 🐷 渲染预览图
    // 获取上传文件表单元素的DOM对象，并注册change事件
    // 😝 此处也需要委托
    $('.proxy-parent').on('change', '#preImg', (e) => {
        try {
            // 1.创建reader对象
            const reader = new FileReader()
            // 2.将选中的文件变成base64，将图片作为参数传递进去
            // console.dir(e.target) 里边的files中索引为0的值就是预览图片
            reader.readAsDataURL(e.target.files[0])
            // 3.通过reader.onload获取转换后的图片
            reader.onload = function (e) {
                // console.log(e.target)
                // 因为jQuery并没有src属性，所以要把dom节点拿出来
                $('#showImg')[0].src = e.target.result
            }
        } catch (error) {
            // 如果不上传图片，就不显示图片
            $('#showImg')[0].src = ''
        }
    })

    // 🐷 删除广告
    // 😡😡 注意，注意，当我们写好代码运行的时候，会发现只能删除一次，之后再删除数据就删除不掉了
    // 😡😡 因为当我们请求完成之后，会重新渲染一遍advList.ejs,我们获取的都是渲染之前的节点，
    // 😡😡 而重新渲染过后的advList.ejs为新节点，我们获取不到，为了解决这一问题，我们使用事件委托
    // 😋 我们在index.ejs中找到一个重新渲染但不会被覆盖的父节点
    // 😝 jQuery中事件委托的写法 前边是被委托的节点(一般为父节点) 后边是委托的节点(子节点)  
    $('.proxy-parent').on('click', '.adv-del-btn', async (e) => {
        // 1.要删除哪一条数据？(用id来确认要删除的数据)
        const advId = e.target.id
        // 😡 注意:此处有一个点需要注意
        // 😡 即发送的请求要传输两个ID和一个token,因此注意管理员的ID和广告的ID(advId)不能相同，避免混乱
        // 2.发送axios请求，并将id作为参数传进去
        const result = await DelAdv(advId)
        // 3.如果删除成功，提示用户
        toastr.error(result.msg)
        // 4.并且重新渲染页面
        res.render(advList(result.advs))
    })
    // 修改广告
    $('.proxy-parent').on('click', '.updata-adv', async (e) => {
        // 1.首先要修改模态框的标题和按钮上的文字
        $('#mt').text('修改广告')
        $('#addOrUpdateAdv').text('修改')
        // 2.当点击了该按钮之后，发送一次请求,但是发送请求需要商品的ID，因此，在添加广告的时候就把商品id传入到修改按钮中
        const advId = e.target.dataset.id
        // 3.封装发送单次请求的函数，并且定义后端接口
        const { adv } = await GetOneAdv(advId)
        // 4.当获取到该条数据后，将数据渲染到模态框
        $('input[name=advTitle]').val(adv.advTitle)
        $('input[name=advCate]').val(adv.advCate)
        $('input[name=advLink]').val(adv.advLink)
        $('#showImg')[0].src = adv.advImg
        // 5.此时我们发现 添加广告的模态框的数据也被渲染了，所以要去修改一下📌
        // 6.🚩在146行
        // 给当前作用域的updataAdvId赋值
        updataAdvId = advId
    })
    // 添加广告
    $('.proxy-parent').on('click', '#add-adv', () => {
        $('#mt').text('添加广告')
        $('#addOrUpdateAdv').text('添加')
        // 📌 来这里修改
        $('#form')[0].reset()
        $('#showImg')[0].src = ''
    })
})
// ✨ 如果所有的url地址都没有匹配上，则重定向到登陆页面
router.route('*', (req, res) => {
    res.redirect('/login')
})